package beer.xz.beer.device.comm.serial

import beer.xz.beer.device.comm.protocol.BeerMachineStatus
import beer.xz.beer.device.comm.protocol.BeerProtocolImpl
import com.fazecast.jSerialComm.SerialPort
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import java.io.IOException
import java.util.concurrent.ConcurrentLinkedQueue

/**
 * 处理与啤酒机的串口通信
 */
class BeerSerialHandler(
    private val portName: String,
    private val baudRate: Int = 115200,  // 根据协议规定
    private val dataBits: Int = 8,
    private val stopBits: Int = SerialPort.ONE_STOP_BIT,
    private val parity: Int = SerialPort.NO_PARITY
) {
    private var serialPort: SerialPort? = null

    private val scope = CoroutineScope(Dispatchers.IO)
    private var readJob: Job? = null

    // Buffer for incoming data
    private val buffer = ConcurrentLinkedQueue<Byte>()

    // Protocol implementation
    private val protocol = BeerProtocolImpl()

    // Flow for received data
    private val _dataFlow = MutableSharedFlow<BeerMachineStatus>(replay = 0)
    val dataFlow: SharedFlow<BeerMachineStatus> = _dataFlow

    /**
     * 打开串口
     * @return 如果成功打开串口返回true，否则返回false
     */
    fun open(): Boolean {
        try {
            serialPort = SerialPort.getCommPort(portName).apply {
                baudRate = this@BeerSerialHandler.baudRate
                setComPortParameters(
                    this@BeerSerialHandler.baudRate,
                    this@BeerSerialHandler.dataBits,
                    this@BeerSerialHandler.stopBits,
                    this@BeerSerialHandler.parity
                )
                setComPortTimeouts(
                    SerialPort.TIMEOUT_READ_SEMI_BLOCKING,
                    1000,
                    1000
                )
            }

            val result = serialPort?.openPort() == true
            if (result) {
                // 启动读取线程
                startReading()
            }
            return result
        } catch (e: Exception) {
            e.printStackTrace()
            return false
        }
    }

    /**
     * 关闭串口
     */
    fun close() {
        // 停止读取线程
        readJob?.cancel()
        readJob = null

        // 关闭串口
        serialPort?.closePort()
        serialPort = null

        // 清空缓冲区
        buffer.clear()
    }

    /**
     * 发送指令数据到啤酒机
     * @param commandData 指令数据
     * @return 发送是否成功
     */
    fun sendCommandData(commandData: ByteArray): Boolean {
        val port = serialPort ?: return false

        try {
            val bytesWritten = port.writeBytes(commandData, commandData.size.toLong())
            return bytesWritten.toInt() == commandData.size
        } catch (e: Exception) {
            e.printStackTrace()
            return false
        }
    }


    /**
     * Starts reading data from the serial port.
     */
    private fun startReading() {
        readJob = scope.launch {
            val port = serialPort ?: return@launch
            val readBuffer = ByteArray(1024)

            while (true) {
                try {
                    val bytesRead = port.readBytes(readBuffer, readBuffer.size.toLong())

                    if (bytesRead > 0) {
                        // Add bytes to buffer
                        for (i in 0 until bytesRead) {
                            buffer.add(readBuffer[i])
                        }

                        // Process complete packets
                        processBuffer()
                    }
                } catch (e: IOException) {
                    e.printStackTrace()
                    break
                }
            }
        }
    }

    /**
     * Processes the buffer to extract complete packets.
     */
    private fun processBuffer() {
        // Look for header and tail bytes
        val bufferArray = buffer.toTypedArray()
        var headerIndex = -1
        var tailIndex = -1

        // Find header byte
        for (i in bufferArray.indices) {
            if (bufferArray[i] == BeerProtocolImpl.HEADER_BYTE) {
                headerIndex = i
                break
            }
        }

        // If header found, look for tail bytes (0x0D, 0x0A)
        if (headerIndex != -1) {
            for (i in headerIndex + 1 until bufferArray.size - 1) {
                if (bufferArray[i] == BeerProtocolImpl.TAIL_BYTE1 &&
                    i + 1 < bufferArray.size &&
                    bufferArray[i + 1] == BeerProtocolImpl.TAIL_BYTE2
                ) {
                    tailIndex = i + 1 // Include both tail bytes
                    break
                }
            }
        }

        if (headerIndex != -1 && tailIndex != -1) {
            // Extract the packet
            val packetSize = tailIndex - headerIndex + 1

            // Check if packet size is valid
            if (packetSize == BeerProtocolImpl.TOTAL_PACKET_SIZE) {
                val packet = ByteArray(packetSize)

                // Remove bytes before header if any
                for (i in 0 until headerIndex) {
                    buffer.poll()
                }

                // Extract the packet
                for (i in 0 until packetSize) {
                    packet[i] = buffer.poll()
                }

                // Decode the packet
                val result = protocol.decodeData(packet)

                if (result != null) {
                    // Emit the data
                    scope.launch {
                        _dataFlow.emit(result)
                    }
                }
            } else {
                // Invalid packet size, remove header byte and continue
                buffer.poll()
            }
        } else if (buffer.size > 2000) {
            // Buffer is too large, clear it to prevent memory issues
            buffer.clear()
        }
    }


}
